寫完網站後,總不可能總是透過 go run 的指令將網站運行起來,勢必要透過 build 的方式將整個程式封裝誠執行檔,但光是封裝執行檔,對於不同環境必須要 build 出不同檔案,這時候將程式 build 成 docker image 就是一個不錯的選擇,今天就來聊聊怎麼將 golang 程式封裝成 docker image 吧!
還記得在前幾天我們有寫過一個 一對一隨機匿名聊天室 的專案嗎,我們透過此專案當作範例
專案位置:https://github.com/codingXiang/random_anonymous_chat
要使用 Docker 打包必須要透過撰寫 Dockerfile 讓 Docker Engine 知道要做什麼步驟,首先我們先一步一步建構簡單的 Dockerfile,首先,我們在專案的根目錄建立一個名為 Dockerfile 的檔案。
以下步驟都是要寫在 Dockerfile 裡面
要 build go 的專案,勢必一定要有 go 的環境,我們可以在 docker hub 中找到 golang 的環境 image,所以可以在 FROM 的部分寫上 golang
FROM golang
此步驟為定義專案要放在 container 內部的哪一個位置,通常我習慣會放在根目錄底下的 /app 資料夾,因此可以先建立 /app 後,將 WORKDIR 設定到此
RUN     mkdir -p /app
WORKDIR /app
因為要在 Container 內部進行 build,因此我們將整個專案 COPY 到 Container 內
COPY . .
在進行 build 之前,要先將相依的 package 進行下載,可以透過 go mod download 的方式,依照 go.mod 與 go.sum 裡面定義的 package 版本進行下載
RUN     go mod download
接下來就是重頭戲了,將專案透過 go build 的方式打包,這邊我們透過 -o 的方式指定 build 完的 binary 為 app
RUN    go build -o app
build 完畢之後,最後就是要定義在啟動 docker image 時,要執行哪個檔案,這邊是執行 app
ENTRYPOINT ["./app"]
整個 Docker image 如下
FROM        golang
RUN         mkdir -p /app
WORKDIR     /app
COPY        . .
RUN         go mod download
RUN         go build -o app
ENTRYPOINT  ["./app"]
透過 docker build -t 的指令進行打包
docker build -t 'random_anonymous_chat' .
打包過程如下
Sending build context to Docker daemon    276kB
Step 1/7 : FROM        golang
latest: Pulling from library/golang
57df1a1f1ad8: Pull complete 
71e126169501: Pull complete 
1af28a55c3f3: Pull complete 
03f1c9932170: Pull complete 
f4773b341423: Pull complete 
fb320882041b: Pull complete 
24b0ad6f9416: Pull complete 
Digest: sha256:da7ff43658854148b401f24075c0aa390e3b52187ab67cab0043f2b15e754a68
Status: Downloaded newer image for golang:latest
 ---> 05c8f6d2538a
Step 2/7 : RUN         mkdir -p /app
 ---> Running in b236f2a55671
Removing intermediate container b236f2a55671
 ---> a1d4433bc891
Step 3/7 : WORKDIR     /app
 ---> Running in 23926232deef
Removing intermediate container 23926232deef
 ---> 4a29514e9e2e
Step 4/7 : COPY        . .
 ---> 6982af8b156a
Step 5/7 : RUN         go mod download
 ---> Running in 7952a98e18b9
Removing intermediate container 7952a98e18b9
 ---> bfe1a6d44dec
Step 6/7 : RUN         go build -o app
 ---> Running in 1fbd1dc5d3a0
Removing intermediate container 1fbd1dc5d3a0
 ---> e18c00c53eff
Step 7/7 : ENTRYPOINT  ["./app"]
 ---> Running in 5aa4c1da9112
Removing intermediate container 5aa4c1da9112
 ---> 6c61e6e96b4b
Successfully built 6c61e6e96b4b
Successfully tagged random_anonymous_chat:latest
完成後可以透過 docker images 來查看,會看到已經有一個 image 出現拉
REPOSITORY                                           TAG                                              IMAGE ID            CREATED             SIZE
random_anonymous_chat                                latest                                           3e102196a966        4 seconds ago       1.38GB
最後我們可以透過 docker run 的指令來運行封裝好的 image,可以用 -e 來指定環境變數、透過 -p 來指定 container 對外暴露的 port
docker run -e REDIS_URL=172.20.10.2 -p 5000:5000 random_anonymous_chat
可以看到執行的結果如下
2020/09/24 13:42:15 redis 回應成功, PONG
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)
[GIN-debug] Loaded HTML Templates (2): 
        - 
        - index.html
[GIN-debug] GET    /assets/*filepath         --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] HEAD   /assets/*filepath         --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] GET    /                         --> main.main.func1 (3 handlers)
[GIN-debug] GET    /ws                       --> main.main.func2 (3 handlers)
[GIN-debug] Listening and serving HTTP on :5000
這時候開啟瀏覽器,輸入 http://127.0.0.1:5000 就可以看到網站順利運作拉!
今天的範例程式我放在這邊~有興趣的朋友可以 fork 回去玩玩看
https://github.com/codingXiang/random_anonymous_chat/tree/docker
透過 Docker 方式打包程式的好處在於,只要在任何有 Docker engine 的環境中都可以順利的被執行,而且可以確保每次的結果都是一樣的,才不會發生在不同環境執行會有環境的不相依的問題發生。
大家可以發現,build 完的 docker image 足足有 1.38G 這麼大,一定有辦法可以縮減它的大小的!
明天就讓我們繼續使用這個範例將整體的 image size 跟方法做最佳化吧!
請教一下
docker 用going寫網站服務
那麼let's憑證 要如何安裝在docker
讓用戶能在https://xxx.xxx.com 運作正常!!
是否能提點一下
在不動到程式的情況下應該可以透過 docker 架設一台 nginx,把 let's encrypt 的憑證放在 nginx 內部並綁定網址 (xxx.xxx.com 之類的),接著再設定反向代理 將 traffic 轉向 golang 所啟動的 ip:port 即可。
步驟整理一下:
tips : golang 所啟動的 web server 不要把 port 開放對外
這是我的想法~希望可以幫到你
好的!!我試看看